package com.example.lotterysystem.service.impl;

import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.RedisUtil;
import com.example.lotterysystem.controller.param.CreateActivityParam;
import com.example.lotterysystem.controller.param.CreatePrizeByActivityParam;
import com.example.lotterysystem.controller.param.CreateUserByActivityParam;
import com.example.lotterysystem.controller.param.PageParam;
import com.example.lotterysystem.dao.dataobject.ActivityDO;
import com.example.lotterysystem.dao.dataobject.ActivityPrizeDO;
import com.example.lotterysystem.dao.dataobject.ActivityUserDO;
import com.example.lotterysystem.dao.dataobject.PrizeDO;
import com.example.lotterysystem.dao.mapper.*;
import com.example.lotterysystem.service.ActivityService;
import com.example.lotterysystem.service.dto.ActivityCreateDTO;
import com.example.lotterysystem.service.dto.ActivityDTO;
import com.example.lotterysystem.service.dto.ActivityDetailDTO;
import com.example.lotterysystem.service.dto.PageListDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import com.example.lotterysystem.service.enums.ActivityUserStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class ActivityServiceImpl implements ActivityService {

    private static final Logger logger = LoggerFactory.getLogger(ActivityServiceImpl.class);

    //活动存储到redis中key的前缀
    private final String ACTIVITY_PREFIX = "ACTIVITY_";

    private final Long ACTIVITY_TIME_OUT = 60*60*24*3L;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private ActivityUserMapper activityUserMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ActivityCreateDTO createActivity(CreateActivityParam param) {
        //1.校验活动信息
        checkActivityInfo(param);

        //2.保存活动信息
        ActivityDO activityDO=new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);

        //3.保存活动关联的奖品信息
        List<CreatePrizeByActivityParam> prizeParams = param.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOList = prizeParams.stream()
                .map(createPrizeByActivityParam -> {
                    ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeId(createPrizeByActivityParam.getPrizeId());
                    activityPrizeDO.setPrizeAmount(createPrizeByActivityParam.getPrizeAmount());
                    activityPrizeDO.setPrizeTiers(createPrizeByActivityParam.getPrizeTiers());
                    activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return activityPrizeDO;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDOList);

        //4.保存活动关联人员信息
        List<CreateUserByActivityParam> userParams = param.getActivityUserList();
        List<ActivityUserDO> activityUserDOList = userParams.stream()
                .map(userParam ->{
                    ActivityUserDO activityUserDO = new ActivityUserDO();
                    activityUserDO.setActivityId(activityDO.getId());
                    activityUserDO.setUserId(userParam.getUserId());
                    activityUserDO.setUserName(userParam.getUserName());
                    activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
                    return activityUserDO;
                }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserDOList);

        //5.整合活动信息，存放在redis中，为以后的抽将做准备
        //ActivityDetailDTO：活动+人员+奖品

        //由于ActivityDetailDTO的一些关于活动的基本属性在奖品活动关联表中不存在，在活动表中存在，所以，我们要先从数据库中去查询，获取prize的基本信息
        List<Long> prizeIds = activityPrizeDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .distinct()
                .collect(Collectors.toList());

        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);
        ActivityDetailDTO detailDTO=convertToActivityDetailDTO(activityDO,prizeDOList,activityPrizeDOList,activityUserDOList);

        //将整合的活动信息存储在redis中
        cacheActivity(detailDTO);

        //构造返回
        ActivityCreateDTO activityCreateDTO = new ActivityCreateDTO();
        activityCreateDTO.setActivityId(activityDO.getId());
        return activityCreateDTO;
    }


    /**
     * 获取活动列表
     * @param param
     * @return
     */
    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam param) {
        if (param == null){
            throw new ServiceException(ServiceErrorCodeConstants.FIND_ACTIVITY_ERROR);
        }
        //1.查询总量
        Integer total = activityMapper.count();
        //2.从表中获取活动数据
        List<ActivityDO> activityListDO=activityMapper.selectActivityList(param.offset(),param.getPageSize());
        List<ActivityDTO> activityListDTO = new ArrayList<>();
        for (ActivityDO activityDO : activityListDO){
            ActivityDTO activityDTO = new ActivityDTO();
            activityDTO.setActivityId(activityDO.getId());
            activityDTO.setActivityName(activityDO.getActivityName());
            activityDTO.setDescription(activityDO.getDescription());
            activityDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
            activityListDTO.add(activityDTO);
        }
        return new PageListDTO<ActivityDTO>(total,activityListDTO);
    }

    /**
     * 查询活动的详细信息
     * @param activityId
     * @return
     */
    @Override
    public ActivityDetailDTO findActivityDetail(Long activityId) {
        if (activityId == null){
            logger.warn("获取活动详细信息失败，activityId：{}",activityId);
        }
        //1.先从redis中获取
        ActivityDetailDTO activityDetailDTO = getActivityFromCache(activityId);
        if (activityDetailDTO != null){
            logger.info("获取活动详情成功！activityDetailDTO：{}",activityDetailDTO);
            return activityDetailDTO;
        }
        //2.没有从redis查到，此时查表
        //活动表
        ActivityDO aDO = activityMapper.selectById(activityId);
        //活动奖品表
        List<ActivityPrizeDO> apDOList = activityPrizeMapper.selectByActivityId(activityId);
        //活动人员表
        List<ActivityUserDO> auDOList = activityUserMapper.selectByActivityId(activityId);
        //奖品表,要先获取到奖品id
        List<Long> prizeIds = apDOList.stream().map(ActivityPrizeDO::getPrizeId).collect(Collectors.toList());
        List<PrizeDO> pDOList = prizeMapper.batchSelectByIds(prizeIds);

        //整合DO信息
        activityDetailDTO = convertToActivityDetailDTO(aDO,pDOList,apDOList,auDOList);
        //将activityDetailDTO存储到redis中
        cacheActivity(activityDetailDTO);
        return activityDetailDTO;
    }

    @Override
    public void cacheActivity(Long activityId) {
        if (activityId == null){
            logger.warn("要缓存的活动id为空！");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }
        //活动表
        ActivityDO aDO = activityMapper.selectById(activityId);
        if (aDO == null){
            logger.error("要缓存的活动id有误！");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_ERROR);
        }
        //活动奖品表
        List<ActivityPrizeDO> apDOList = activityPrizeMapper.selectByActivityId(activityId);
        //活动人员表
        List<ActivityUserDO> auDOList = activityUserMapper.selectByActivityId(activityId);
        //奖品表,要先获取到奖品id
        List<Long> prizeIds = apDOList.stream().map(ActivityPrizeDO::getPrizeId).collect(Collectors.toList());
        List<PrizeDO> pDOList = prizeMapper.batchSelectByIds(prizeIds);

        //整合DO信息
       /* ActivityDetailDTO activityDetailDTO = convertToActivityDetailDTO(aDO,pDOList,apDOList,auDOList);*/
        //将activityDetailDTO存储到redis中
        cacheActivity(
                convertToActivityDetailDTO(aDO,pDOList,apDOList,auDOList));
    }

    /**
     * 将整合的活动信息存储到redis中
     * @param detailDTO
     */
    private void cacheActivity(ActivityDetailDTO detailDTO) {
        //缓存的key ACTIVITY_ 作为前缀+activityId
        if (detailDTO == null || detailDTO.getActivityId() == null){
            logger.warn("活动的存储信息为空！cacheActivity：{}", JacksonUtil.writeValueAsString(detailDTO));
        }
        try {
            redisUtil.set(ACTIVITY_PREFIX+detailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(detailDTO),
                    ACTIVITY_TIME_OUT);
        }catch (Exception e){
            logger.error("活动缓存失败 ActivityDetailDTO：{}",
                    JacksonUtil.writeValueAsString(detailDTO),
                    e);
        }

    }

    /**
     * 从缓存中获取活动信息
     * @param activityId
     * @return
     */
    private ActivityDetailDTO getActivityFromCache(Long activityId){
        if (activityId == null){
            logger.info("获取缓存活动数据的activityId为null");
        }
        try {
            String str=redisUtil.get(ACTIVITY_PREFIX+activityId);
            if (!StringUtils.hasText(str)){
                logger.info("获取缓存活动数据的str为null，key：{}",ACTIVITY_PREFIX+activityId);
                return null;
            }
            ActivityDetailDTO detailDTO = JacksonUtil.readValue(str,ActivityDetailDTO.class);
            return detailDTO;
        }catch (Exception e){
            logger.error("获取活动信息失败！key={}",ACTIVITY_PREFIX+activityId,e);
            return null;
        }

    }

    /**
     * 整合活动信息
     * @param activityDO
     * @param prizeDOList
     * @param activityPrizeDOList
     * @param activityUserDOList
     * @return
     */
    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO,
                                           List<PrizeDO> prizeDOList,
                                           List<ActivityPrizeDO> activityPrizeDOList,
                                           List<ActivityUserDO> activityUserDOList) {
        //整合活动相关信息
        ActivityDetailDTO activityDetailDTO =new ActivityDetailDTO();
        activityDetailDTO.setActivityId(activityDO.getId());
        activityDetailDTO.setActivityName(activityDO.getActivityName());
        activityDetailDTO.setDesc(activityDO.getDescription());
        activityDetailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));

        //整合奖品相关信息
        //apDO:{prizeID,amount,status},{prizeID,amount,status}
        //prizeDo:{prizeId,name......},{prizeId,name......}
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList =  activityPrizeDOList.stream()
                        .map(apDO ->{
                            ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                            prizeDTO.setPrizeId(apDO.getPrizeId());
                            Optional<PrizeDO> optionalPrizeDO= prizeDOList.stream()
                                    .filter(prizeDO -> prizeDO.getId().equals(apDO.getPrizeId()))
                                    .findFirst();
                            //如果prizeDO为空，则不执行ifPresent方法，不为空才执行ifPresent方法
                            optionalPrizeDO.ifPresent(prizeDO -> {
                                prizeDTO.setDescription(prizeDO.getDescription());
                                prizeDTO.setImageUrl(prizeDO.getImageUrl());
                                prizeDTO.setName(prizeDO.getName());
                                prizeDTO.setPrice(prizeDO.getPrice());
                            });
                            prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(apDO.getPrizeTiers()));
                            prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(apDO.getStatus()));
                            prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
                            return prizeDTO;
                        }).collect(Collectors.toList());
        activityDetailDTO.setPrizeDTOList(prizeDTOList);

        //整合人员相关信息
        List<ActivityDetailDTO.UserDTO> userDTOList = activityUserDOList.stream()
                        .map(activityUserDO -> {
                            ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                            userDTO.setUserId(activityUserDO.getUserId());
                            userDTO.setUserName(activityUserDO.getUserName());
                            userDTO.setStatus(ActivityUserStatusEnum.forName(activityUserDO.getStatus()));
                            return userDTO;
                        }).collect(Collectors.toList());
        activityDetailDTO.setUserDTOList(userDTOList);
        return activityDetailDTO;
    }

    /**
     * 校验活动有效性
     * @param param
     */
    private void checkActivityInfo(CreateActivityParam param) {
        if (param == null){
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_EMPTY);
        }
        //1.校验人员是否在user表中,通过id去查询
        List<Long> userIds=param.getActivityUserList().stream()
                            .map(CreateUserByActivityParam::getUserId)
                            .distinct() //去重
                            .collect(Collectors.toList());
        List<Long> existUserIds=userMapper.selectExistByIds(userIds);
        if (CollectionUtils.isEmpty(existUserIds)){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_INFO_ERROR);
        }
        userIds.forEach(id -> {
            if (!existUserIds.contains(id)){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_INFO_ERROR);
            }
        });
        //2.校验奖品id是否存在prize表中，根据id去表中查
        List<Long> prizeIds = param.getActivityPrizeList().stream()
                                    .map(CreatePrizeByActivityParam::getPrizeId)
                                    .distinct()
                                    .collect(Collectors.toList());
        List<Long> existPrizeIds = prizeMapper.selectExistPrizeById(prizeIds);
        if (CollectionUtils.isEmpty(existPrizeIds)){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_INFO_ERROR);
        }
        prizeIds.forEach(id->{
            if (!existPrizeIds.contains(id)){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_INFO_ERROR);
            }
        });
        //3.人员数量要大于等于奖品数量
        long userAmount=param.getActivityUserList().size();
        long prizeAmount = param.getActivityPrizeList()
                                    .stream()
                                    .mapToLong(CreatePrizeByActivityParam::getPrizeAmount)
                                    .sum();
        if (userAmount < prizeAmount){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_USER_INFO_ERROR);
        }

        //4.判断奖品等级是否正确
        param.getActivityPrizeList().forEach(prize ->{
            if (ActivityPrizeTiersEnum.forName(prize.getPrizeTiers())==null){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIER_ERROR);
            }
        });

    }
}
